package com.ylr.blog.config;

import com.ylr.base.common.constant.Constant;
import com.ylr.base.common.factory.YlrThreadFactory;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * @author wei
 * date: 2021-09-14 14:42:00
 * className: ThreadConfig 线程池配置类
 * version: 1.0
 * description:
 */
@Getter
@Setter
@Configuration
public class ThreadConfig {

    /**
     * 线程池核心线程数
     * 如果配置文件没有配置，则给定一个‘10’默认值
     */
    @Value("${thread-config.core-size:10}")
    private Integer coreSize;

    /**
     * 线程池最大线程数
     * 如果配置文件没有配置，则给定一个‘20’默认值
     */
    @Value("${thread-config.max-size:20}")
    private Integer maxSize;

    /**
     * 线程池的任务队列容量
     * 如果配置文件没有配置，则给定一个‘1000’默认值
     */
    @Value("${thread-config.queue-capacity:1000}")
    private Integer queueCapacity;

    /**
     * 线程池线程名称前缀
     * 如果配置文件没有配置，则给定一个‘blog-thread-’默认值
     */
    @Value("${thread-config.thread-name-prefix:blog-thread-}")
    private String threadNamePrefix;

    /**
     * 任务拒绝策略：
     *  0-CallerRunsPolicy（调用线程同步执行）
     *  1-AbortPolicy（拒绝任务，报异常）
     *  2-DiscardPolicy（丢弃新任务，不报异常）
     *  3-DiscardOldestPolicy（丢弃旧任务，放入新任务到任务队列中）
     * <p>
     * 如果配置文件没有配置，则给定一个‘0’默认值
     */
    @Value("${thread-config.reject-type:0}")
    private Integer rejectType;

    /**
     * 将自定义ThreadPoolTaskExecutor线程池添加到Spring容器中
     */
    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        /*
            1、线程池非核心线程存活时间采用默认的60秒
            2、核心线程跑满后，新任务加入队列，且队列达到容量阈值，才会创建非核心线程执行新任务
            3、非核心线程被创建后，空闲时间达到默认值后，将被系统回收
            4、如果任务队列容量大于0，底层使用LinkedBlockingQueue作为任务队列，否则使用SynchronousQueue作为任务队列
            5、ThreadPoolTaskExecutor线程池采用‘装饰’设计模式，对JDK自带的ThreadPoolExecutor线程池进行二次封装，线程池执行逻辑仍然采用ThreadPoolExecutor的代码
         */
        // 设置线程池核心线程数
        executor.setCorePoolSize(coreSize);
        // 设置线程池持有最大线程数
        executor.setMaxPoolSize(maxSize);
        // 设置任务队列大小
        executor.setQueueCapacity(queueCapacity);
        // 设置线程工厂
        executor.setThreadFactory(new YlrThreadFactory(threadNamePrefix));
        // 设置线程名称前缀
        executor.setThreadNamePrefix(threadNamePrefix);
        // 设置线程池任务拒绝策略
        executor.setRejectedExecutionHandler(YlrThreadFactory.getRejectedExecutionHandler(rejectType));
        // 开启链路追踪，对异步线程进行装饰
        executor.setTaskDecorator(YlrThreadFactory.getTaskDecorator());

        // 初始化线程池
        executor.initialize();

        return executor;
    }

    /**
     * 将自定义ThreadPoolTaskExecutor线程池添加到Spring容器中
     */
    @Bean
    public ScheduledThreadPoolExecutor scheduledThreadPoolExecutor() {

        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(coreSize);
        // 设置线程生产工厂对象
        executor.setThreadFactory(new YlrThreadFactory(Constant.DELAY_THREAD_NAME_PREFIX));

        return executor;
    }

}
